/***********************************************************************************************************************
*  OpenStudio(R), Copyright (c) Alliance for Sustainable Energy, LLC.
*  See also https://openstudio.net/license
***********************************************************************************************************************/

#ifndef GBXML_FORWARDTRANSLATOR_HPP
#define GBXML_FORWARDTRANSLATOR_HPP

#include "gbXMLAPI.hpp"

#include "../utilities/core/Path.hpp"
#include "../utilities/core/Optional.hpp"
#include "../utilities/core/Logger.hpp"
#include "../utilities/core/StringStreamLogSink.hpp"
// This is needed to declare the set<Material, IdfObjectImplLess>, so everything is a complete type
#include "../utilities/idf/IdfObject.hpp"
#include "../model/Material.hpp"

#include "../model/ModelObject.hpp"

#include <map>

namespace pugi {
class xml_node;
class xml_document;
}  // namespace pugi

namespace openstudio {

class ProgressBar;
class Transformation;

namespace model {
  class Model;
  class ConstructionBase;
  class Facility;
  class Building;
  class BuildingStory;
  class ThermalZone;
  class Space;
  class ShadingSurfaceGroup;
  class BuildingStory;
  class Surface;
  class SubSurface;
  class ShadingSurface;
}  // namespace model

namespace gbxml {

  class GBXML_API ForwardTranslator
  {
   public:
    ForwardTranslator();

    ~ForwardTranslator();

    // Save the GbXML to a file
    bool modelToGbXML(const openstudio::model::Model& model, const openstudio::path& path, ProgressBar* progressBar = nullptr);

    // Return a string representation of the GbXML document
    std::string modelToGbXMLString(const openstudio::model::Model& model, ProgressBar* progressBar = nullptr);

    /** Get warning messages generated by the last translation. */
    std::vector<LogMessage> warnings() const;

    /** Get error messages generated by the last translation. */
    std::vector<LogMessage> errors() const;

   private:
    std::string escapeName(const std::string& name);

    // listed in translation order
    bool translateModel(const openstudio::model::Model& model, pugi::xml_document& document);

    // Facility and Building could not be explicitly instantiated in the model, but the functions still need to be called so that Spaces and surfaces
    // are translated. Facility and Building both are UniqueModelObjects, so passing model here as an argument is harmless
    boost::optional<pugi::xml_node> translateFacility(const openstudio::model::Model& model, pugi::xml_node& parent);
    boost::optional<pugi::xml_node> translateBuilding(const openstudio::model::Model& model, pugi::xml_node& parent);

    boost::optional<pugi::xml_node> translateSpace(const openstudio::model::Space& space, pugi::xml_node& parent);
    boost::optional<pugi::xml_node> translateShadingSurfaceGroup(const openstudio::model::ShadingSurfaceGroup& shadingSurfaceGroup,
                                                                 pugi::xml_node& parent);
    boost::optional<pugi::xml_node> translateBuildingStory(const openstudio::model::BuildingStory& story, pugi::xml_node& parent);
    boost::optional<pugi::xml_node> translateSurface(const openstudio::model::Surface& surface, pugi::xml_node& parent);
    boost::optional<pugi::xml_node> translateSubSurface(const openstudio::model::SubSurface& subSurface,
                                                        const openstudio::Transformation& transformation, pugi::xml_node& parent);
    boost::optional<pugi::xml_node> translateShadingSurface(const openstudio::model::ShadingSurface& shadingSurface, pugi::xml_node& parent);
    boost::optional<pugi::xml_node> translateThermalZone(const openstudio::model::ThermalZone& thermalZone, pugi::xml_node& parent);
    boost::optional<pugi::xml_node> translateLayer(const openstudio::model::Material& material, pugi::xml_node& parent);
    boost::optional<pugi::xml_node> translateMaterial(const openstudio::model::Material& material, pugi::xml_node& parent);
    boost::optional<pugi::xml_node> translateConstructionBase(const openstudio::model::ConstructionBase& constructionBase, pugi::xml_node& parent);

    /** Set id as the model object name, otherwise use the gbXMLId additional property if it exists. */
    void translateId(const openstudio::model::ModelObject& modelObject, pugi::xml_node& parentElement);

    /** Set the Name as the model object name, otherwise use the displayName additional property if it exists. */
    void translateName(const openstudio::model::ModelObject& modelObject, pugi::xml_node& parentElement);

    /** Set the CADObjectId as the CADObjectId additional property if it exists. */
    boost::optional<pugi::xml_node> translateCADObjectId(const openstudio::model::ModelObject& modelObject, pugi::xml_node& parentElement);

    std::map<openstudio::Handle, pugi::xml_node> m_translatedObjects;

    std::set<openstudio::model::Material, openstudio::IdfObjectImplLess> m_materials;

    // Assigning a Shading Surface Construction is mostly pointless, so to avoid gbXML validation errors, we create one if need be
    bool m_placeholderShadingSurfaceConstructionAlreadyCreated = false;

    StringStreamLogSink m_logSink;

    ProgressBar* m_progressBar;

    REGISTER_LOGGER("openstudio.gbxml.ForwardTranslator");
  };

}  // namespace gbxml
}  // namespace openstudio

#endif  // GBXML_FORWARDTRANSLATOR_HPP
